
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Threading;
using Swaf;
using Swaf.Container;

namespace Swaf.Versioning
{
	/// <summary>
	/// This class provides the base implementation for versioning dictionaries. All puts into this dictionary go into a single
	/// base storage location.  Gets lookup against a cache storage first, then fall back to the base storage location if 
	/// the requested versioned key is not found.  Once an item is found in the base storage, it is added to the cache using
	/// the requested versioned string.
	/// </summary>
	[Serializable]
	public abstract class BaseVersioningDictionary :IDictionary
	{
		protected IDictionary m_Source;
		protected IDictionary m_Cache;
		protected BaseVersioningPartner m_versioning;
		protected IApplication m_app;
		protected string m_versioningDelimiter;
		protected string m_expiretype=null;
		/// <summary>
		/// Constructor for the BaseVersioningDictionary class.
		/// </summary>
		public BaseVersioningDictionary()
		{
			m_app = Application.currentApp;
			m_versioning = (BaseVersioningPartner) m_app.appData[@"sysObjs-Versioning"];
			m_versioningDelimiter=BaseVersioningPartner.VERSION_DELIMITER;
			string expire = m_app.resMgr.resolve("$VersioningExpire$");
			if (! expire.Equals("$VersioningExpire$"))
				m_expiretype=expire;
		}
		#region Members to be overridden
		/// <summary>
		/// This method is overridden by the concrete implementation of this class and provides threadsafe read access to the Base storage.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		protected abstract object readFromSource(string key);
		/// <summary>
		/// This method is overridden by the concrete implementation of this class and provides threadsafe write access to the Base storage.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		protected abstract void writeToSource(string key, object value, object extraInfo);
		/// <summary>
		/// This method is overridden by the concrete implementation of this class and provides threadsafe read access to the Cache storage.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		protected abstract object readFromCache(string key);
		/// <summary>
		/// This method is overridden by the concrete implementation of this class and provides threadsafe write access to the Cache storage.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		protected abstract void writeToCache(string key, object value, object extraInfo);

		
		protected virtual IEnumerator getEnumerator()
		{
			return m_Source.GetEnumerator();
		}
		#endregion
		/// <summary>
		/// This method provides the base implementation of the get algorithm.  It is called by any implemented methods that retrieve 
		/// data from the dictionary.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		protected object getValue(string key)
		{
			string newKey = key;
			string orgResolvedKey;
			object retVal = null;
			bool isLoadingConfig=false;
			//See if we have a reference to a versioning partner
			if (m_versioning != null)
			{
				isLoadingConfig=m_versioning.IsConfigLoading;
				if (! isLoadingConfig)
				{
					string callContext = Cmn.getCaller(3);
					//Resolve the requested key with the versioning partner to get the exact versioned key.
					newKey = m_versioning.resolve(key);
				}
			}
			orgResolvedKey = newKey;
			if (! isLoadingConfig)
			{
				//Look in cache for the requested value
				retVal = readFromCache(newKey);

				if (retVal == null) 
				{
					//If the value was not found in cache, look it up in the source.  Keep removing 1 delimited value until
					//either the key is "" or a value is found
					while ((!newKey.Equals("")) && (retVal == null)) 
					{
						//Try to get the value from the source by this key.
						retVal = readFromSource(newKey);
						//If we do not find anything, truncate the key and continue.
						if (retVal == null)
						{
							//Remove the last delimited value from newKey and continue
							int delimiterLocation = newKey.LastIndexOf(m_versioningDelimiter);
							if (delimiterLocation > 0)
							{
								newKey = newKey.Substring(0,delimiterLocation);
							}
							else
							{
								//There are no more delimited values, set new key to "" so we can exit from the loop.
								newKey = "";
							}
						}
					}
					//At this point, if retVal is not null, we have found the value
					//Now we need to cache this value by the requested, resolved key.
					if (retVal != null) 
					{
						writeToCache(orgResolvedKey,retVal, null);
					}
				}
			}
			else
			{
				retVal = readFromSource(key);
			}
			return retVal;
		}
		#region IDictionary Members

		/// <summary>
		/// Overridden IDictionary IsReadOnly property.
		/// </summary>
		public bool IsReadOnly
		{
			get
			{
				return false;
			}
		}

		/// <summary>
		/// Overridden IDictionary GetEnumerator method. This method is implemented as virtual so that it can be
		/// overridden by specific implementations of the VersioningDictionary.
		/// </summary>
		/// <returns></returns>
		public virtual IDictionaryEnumerator GetEnumerator()
		{
			return m_Source.GetEnumerator();
		}

		public object this[object key]
		{
			get
			{
				return getValue((string) key);
			}
			set
			{
				writeToSource((string) key, value, null);
			}
		}

		/// <summary>
		/// Overridden IDictionary Remove method. This method is implemented as virtual so that it can be
		/// overridden by specific implementations of the VersioningDictionary.
		/// </summary>
		/// <param name="key"></param>
		public virtual void Remove(object key)
		{
			m_Source.Remove((string) key);
			/** If removing an item, we need to clear the cache because we are not sure how many references to this
			 ** item exist under different keys
			 **/
			m_Cache.Clear();
		}

		/// <summary>
		/// Overridden IDictionary Contains method. This method is implemented as virtual so that it can be
		/// overridden by specific implementations of the VersioningDictionary.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public virtual bool Contains(object key)
		{
			//We must check both the cache and the source to see if the requested value exists.
			return m_Source.Contains((string) key)  || m_Cache.Contains(key);
		}

		/// <summary>
		/// Overridden IDictionary Clear method. This method is implemented as virtual so that it can be
		/// overridden by specific implementations of the VersioningDictionary.
		/// </summary>
		public virtual void Clear()
		{
			m_Source.Clear();
			m_Cache.Clear();
		}

		/// <summary>
		/// Overridden IDictionary Values property. This property is implemented as virtual so that it can be
		/// overridden by specific implementations of the VersioningDictionary.
		/// </summary>
		public virtual ICollection Values
		{
			get
			{
				return m_Source.Values;
			}
		}

		/// <summary>
		/// Overridden IDictionary Add method. This method is implemented as virtual so that it can be
		/// overridden by specific implementations of the VersioningDictionary.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public virtual void Add(object key, object value)
		{
			writeToSource((string) key, value, null);
		}

		/// <summary>
		/// Overridden IDictionary Keys method. This method is implemented as virtual so that it can be
		/// overridden by specific implementations of the VersioningDictionary.
		/// </summary>
		public virtual ICollection Keys
		{
			get
			{
				return m_Source.Keys;
			}
		}

		/// <summary>
		/// Overridden IDictionary IsFixedSize method.
		/// </summary>
		public bool IsFixedSize
		{
			get
			{
				return false;
			}
		}

		#endregion

		#region ICollection Members

		/// <summary>
		/// Overridden ICollection IsSynchronized property.
		/// </summary>
		public bool IsSynchronized
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// Overridden ICollection Count property. This property is implemented as virtual so that it can be
		/// overridden by specific implementations of the VersioningDictionary.
		/// </summary>
		public virtual int Count
		{
			get
			{
				return m_Source.Count;
			}
		}

		/// <summary>
		/// Overridden ICollection CopyTo method. This method is implemented as virtual so that it can be
		/// overridden by specific implementations of the VersioningDictionary.
		/// </summary>
		/// <param name="array"></param>
		/// <param name="index"></param>
		public virtual void CopyTo(Array array, int index)
		{
			m_Source.CopyTo(array, index);
		}

		/// <summary>
		/// Overridden ICollection SyncRoot property.
		/// </summary>
		public object SyncRoot
		{
			get
			{
				return this;
			}
		}

		#endregion

		#region IEnumerable Members

		/// <summary>
		/// Overridden IEnumerable GetEnumerator method.
		/// </summary>
		/// <returns></returns>
		IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return getEnumerator();
		}

		#endregion	
	}

}
